﻿using System.Collections.Generic;
using System.Linq;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using MongoScienceDAL.Core.Exceptions;

namespace MongoScienceDAL.Core
{
    public class DataSource : IDataSource
    {
        private string ScienceTypesCollectionName { get { return "scienceTypes"; } }
        private string ScienceCollectionsCollectionName { get { return "scienceCollections"; } }

        public MongoDatabase Database { get; private set; }

        public IDictionary<string,string> RestrictedCollectionNames { get; set; }

        public DataSource(MongoDatabase mongoDatabase)
        {
            Database = mongoDatabase;

            //Register restricted dataSource names
            RestrictedCollectionNames = new Dictionary<string, string>
                {
                    {ScienceTypesCollectionName, "Built in Collection for MongoScienceDAL"},
                    {ScienceCollectionsCollectionName, "Built in Collection for MongoScienceDAL"}
                };

            //Loading existing collections into the restricted collection names register
            QueryCollections().Select(x => x.Name).ToList()
                .ForEach(name => RestrictedCollectionNames.Add(name, "Collection already exists."));
        }
        
        public ScienceCollection AddCollection(ScienceCollection collection)
        {
            //Check the collection name for duplicates
            if (RestrictedCollectionNames.ContainsKey(collection.Name))
                throw new InvalidCollectionNameException(RestrictedCollectionNames[collection.Name]);
                
            //Add the new collection reference if it dosent exist
            if (!Database.CollectionExists(ScienceCollectionsCollectionName))
                Database.CreateCollection(ScienceCollectionsCollectionName);

            Database.GetCollection <ScienceCollection>(ScienceCollectionsCollectionName).Insert(collection);

            //Add a entry to the restricted collection names register
            RestrictedCollectionNames.Add(collection.Name, "Collection already exists.");

            return collection;
        }

        public IQueryable<ScienceCollection> QueryCollections()
        {
            return Database.GetCollection<ScienceCollection>(ScienceCollectionsCollectionName).AsQueryable();
        }

        public IQueryable<ScienceType> QueryCollectionTypes(string collectionName)
        {
            //find collection id
            var collections = QueryCollections().Where(x => x.Name == collectionName).ToList();
            if (collections.Count <= 0)
                throw new NoSuchCollectionException();

            var collectionId = collections.First().Id;

            //Filter science types by collection id and return
            return Database.GetCollection<ScienceType>(ScienceTypesCollectionName)
                    .AsQueryable()
                    .Where(x => x.Collection == collectionId)
                    .AsQueryable();
        }
        
        public ScienceType AddCollectionType(string collectionName, ScienceType type)
        {
            //find collection id
            var collections = QueryCollections().Where(x => x.Name == collectionName).ToList();
            if (collections.Count <= 0)
                throw new NoSuchCollectionException();

            var collectionId = collections.First().Id;

            //Assign collection id
            type.Collection = collectionId;

            //Add the new collection reference if it dosent exist
            if (!Database.CollectionExists(ScienceTypesCollectionName))
                Database.CreateCollection(ScienceTypesCollectionName);

            //Insert new type
            Database.GetCollection<ScienceType>(ScienceTypesCollectionName).Insert(type);

            //Return type with its updated Id
            return type;
        }

        public void UpdateCollectionType(ScienceType type)
        {
            //save
            Database.GetCollection<ScienceType>(ScienceTypesCollectionName).Save(type);
        }

        //THE FOLLOW SHOULD BE INIT BEFORE CALLING THIS
        //Register class map
        //ScienceDatabaseClassMap.RegisterTypes();

        //Register serialiser
        //if (BsonSerializer.LookupSerializer(typeof(ScienceEntry)) == null)
            //BsonSerializer.RegisterSerializer(typeof(ScienceEntry), new ScienceEntrySerializer(this));
    }
}
